# Player.gd
class_name Player
extends CharacterBody2D

# Signals
signal health_changed(current_health: float, max_health: float)
signal experience_changed(current_xp: float, level_xp: float)
signal level_up(new_level: int)

# Basic Stats
@export var move_speed: float = 150.0
var base_move_speed: float = 150.0

# Health
var max_health: float = 100.0
var current_health: float = 100.0
var base_max_health: float = 100.0

# Experience & Leveling
var current_xp: float = 0.0
var xp_to_next_level: float = 10.0 # Initial XP needed for level 2
var current_level: int = 1
var level_xp_increase_factor: float = 1.5 # How much more XP is needed per level

# Combat Stats
var armor: float = 0.0 # Flat damage reduction
var crit_chance: float = 0.0 # 0.0 to 1.0
var crit_damage_multiplier: float = 2.0 # e.g., 2.0 for double damage

# Utility Stats
var pickup_range: float = 50.0 # Radius for picking up XP orbs
var base_pickup_range: float = 50.0
var health_regen: float = 0.0 # Health per second
var xp_gain_multiplier: float = 1.0 # Multiplies XP earned

# References
@onready var sprite: Sprite2D = $Sprite2D # Replace with AnimatedSprite2D if you have animations
@onready var collision_shape: CollisionShape2D = $CollisionShape2D
@onready var pickup_area: Area2D = $PickupArea
@onready var pickup_area_shape: CollisionShape2D = $PickupArea/CollisionShape2D
@onready var regen_timer: Timer = $RegenTimer
@onready var weapon_container: Node2D = $WeaponContainer # Node to hold weapon instances

# Weapon Management
var active_weapons: Dictionary = {} # weapon_id: Node (weapon instance)

func _ready():
	
	print("Player Ready at Global Position: ", global_position)
	# Set collision layer/mask in editor (Player on layer 2)
	# Hurtbox (Area2D child) for taking damage (layer 8, mask 5 for enemy_weapon)
	# Hitbox (for potential melee?) (Area2D child) (layer 7, mask 3 for enemies)
	add_to_group("player") # Add player to a group for easy finding
	GameManager.set_player_node(self) # Register with GameManager

	# Initialize stats based on permanent upgrades
	# Example: base_max_health += SaveManager.get_permanent_upgrade_level("base_health_bonus") * 5
	# Recalculate derived stats
	recalculate_stats()

	current_health = max_health

	pickup_area_shape.shape.radius = pickup_range
	regen_timer.wait_time = 1.0
	regen_timer.timeout.connect(_on_regen_timer_timeout)
	regen_timer.start()

	# Connect signals for UI updates (could also use SignalBus)
	emit_signal("health_changed", current_health, max_health)
	emit_signal("experience_changed", current_xp, xp_to_next_level)

	# Connect to SignalBus for global events
	SignalBus.upgrade_chosen.connect(apply_upgrade)

	print("Player Ready. Level:", current_level)
	# TEMP: Grant starting weapon
	grant_weapon("knife")


func _physics_process(delta):
	if GameManager.is_game_over: return
	
	# Movement
	var input_vector = Input.get_vector("move_left", "move_right", "move_up", "move_down")
	velocity = input_vector * move_speed
	move_and_slide()

	# Update pickup area position (it's a child, so it moves with player)
	# Update weapon positions / targeting if needed (handled in weapon scripts)


func take_damage(amount: float):
	if GameManager.is_game_over: return

	var actual_damage = max(0, amount - armor) # Apply armor
	current_health -= actual_damage
	emit_signal("health_changed", current_health, max_health)
	SignalBus.emit_signal("player_health_changed", current_health, max_health) # Global signal

	# Visual feedback (e.g., flash red)
	# $AnimationPlayer.play("take_damage") or modulate sprite

	if current_health <= 0:
		die()


func heal(amount: float):
	current_health = min(current_health + amount, max_health)
	emit_signal("health_changed", current_health, max_health)
	SignalBus.emit_signal("player_health_changed", current_health, max_health)


func _on_regen_timer_timeout():
	if health_regen > 0 and current_health < max_health:
		heal(health_regen)


func add_experience(amount: float):
	if GameManager.is_game_over: return

	current_xp += amount * xp_gain_multiplier
	print("Gained %f XP. Total: %f / %f" % [amount * xp_gain_multiplier, current_xp, xp_to_next_level])

	while current_xp >= xp_to_next_level:
		current_level += 1
		current_xp -= xp_to_next_level
		xp_to_next_level *= level_xp_increase_factor # Increase XP needed for next level
		xp_to_next_level = floor(xp_to_next_level) # Keep it whole if desired

		print("LEVEL UP! Reached Level %d. Next level at %d XP." % [current_level, xp_to_next_level])
		emit_signal("level_up", current_level)
		SignalBus.emit_signal("player_level_up", current_level) # Global signal

	emit_signal("experience_changed", current_xp, xp_to_next_level)
	SignalBus.emit_signal("player_experience_changed", current_xp, xp_to_next_level)


func die():
	print("Player Died!")
	# Stop movement, play death animation, etc.
	set_physics_process(false) # Stop processing movement/physics
	hide() # Or play death animation
	# Emit global signal
	SignalBus.emit_signal("player_died")


func recalculate_stats():
	# Apply permanent upgrades and potentially temporary buffs
	# Example:
	# var health_bonus_level = SaveManager.get_permanent_upgrade_level("base_health_bonus")
	# max_health = base_max_health + (health_bonus_level * 5) # Assuming +5 health per level

	move_speed = base_move_speed # * speed_multiplier_from_buffs etc.
	pickup_range = base_pickup_range # * range_multiplier etc.

	# Update dependent components
	if pickup_area_shape and pickup_area_shape.shape:
		pickup_area_shape.shape.radius = pickup_range

	print("Player stats recalculated. Health:", max_health, "Speed:", move_speed)
	# Emit signals for UI if needed
	SignalBus.emit_signal("player_stat_changed", "max_health", max_health)
	SignalBus.emit_signal("player_stat_changed", "move_speed", move_speed)
	# ... other stats


func apply_upgrade(upgrade: Resource):
	if upgrade is PlayerStatUpgrade:
		apply_player_stat_upgrade(upgrade)
	elif upgrade is WeaponUpgrade:
		apply_weapon_upgrade(upgrade)


func apply_player_stat_upgrade(upgrade: PlayerStatUpgrade):
	print("Applying Player Stat Upgrade:", upgrade.stat_name)
	match upgrade.stat_name:
		"max_health":
			base_max_health += upgrade.value_change # Assume permanent increase to base
			var health_ratio = current_health / max_health if max_health > 0 else 1.0
			recalculate_stats() # Recalculate max_health based on new base
			current_health = max_health * health_ratio # Maintain health percentage
			heal(0) # Clamp health and emit signals
		"move_speed":
			base_move_speed += upgrade.value_change
			recalculate_stats()
		"pickup_range":
			base_pickup_range += upgrade.value_change
			recalculate_stats()
		"health_regen":
			health_regen += upgrade.value_change
			SignalBus.emit_signal("player_stat_changed", "health_regen", health_regen)
		"armor":
			armor += upgrade.value_change
			SignalBus.emit_signal("player_stat_changed", "armor", armor)
		"crit_chance":
			crit_chance = min(1.0, crit_chance + upgrade.value_change) # Clamp at 100%
			SignalBus.emit_signal("player_stat_changed", "crit_chance", crit_chance)
		"xp_gain":
			xp_gain_multiplier += upgrade.value_change # Assume additive percentage, e.g., 0.1 for +10%
			SignalBus.emit_signal("player_stat_changed", "xp_gain", xp_gain_multiplier)
		_:
			printerr("Unknown player stat upgrade: ", upgrade.stat_name)

	# Update UI immediately if needed
	emit_signal("health_changed", current_health, max_health)


func apply_weapon_upgrade(upgrade: WeaponUpgrade):
	print("Applying Weapon Upgrade for:", upgrade.weapon_id, "Type:", upgrade.upgrade_type)
	if upgrade.is_new_weapon:
		grant_weapon(upgrade.weapon_id)
	elif active_weapons.has(upgrade.weapon_id):
		var weapon_node = active_weapons[upgrade.weapon_id]
		if weapon_node and weapon_node.has_method("apply_upgrade"):
			weapon_node.apply_upgrade(upgrade)
			SignalBus.emit_signal("weapon_upgraded", upgrade.weapon_id, upgrade.description)
		else:
			printerr("Weapon node %s missing or cannot apply upgrade." % upgrade.weapon_id)
	else:
		printerr("Cannot apply upgrade: Player does not have weapon %s" % upgrade.weapon_id)


func grant_weapon(weapon_id: String):
	if active_weapons.has(weapon_id):
		print("Weapon %s already owned." % weapon_id)
		# Maybe apply a default upgrade instead?
		return

	# Find weapon data resource (You need to create these .tres files)
	var weapon_data_path = "res://resources/weapons/%s.tres" % weapon_id
	var weapon_data = load(weapon_data_path) as WeaponData

	if not weapon_data or not weapon_data.prefab:
		printerr("Cannot grant weapon: Invalid WeaponData or prefab for ID '%s' at %s" % [weapon_id, weapon_data_path])
		return

	var new_weapon = weapon_data.prefab.instantiate()
	if not new_weapon:
		printerr("Failed to instantiate weapon prefab for %s" % weapon_id)
		return

	# Add weapon to container and track it
	weapon_container.add_child(new_weapon)
	active_weapons[weapon_id] = new_weapon

	# Initialize weapon (pass player ref, weapon data?)
	if new_weapon.has_method("initialize"):
		new_weapon.initialize(self, weapon_data)

	print("Granted new weapon: %s" % weapon_id)
	SignalBus.emit_signal("weapon_added", weapon_id)


# Called by ExperienceOrb when collected
func collect_orb(experience_value: int):
	add_experience(experience_value)
